#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <opencv2/imgproc.hpp>

using namespace std;

bool  ROI_AddImage();
bool  LinearBlending();
bool  ROI_LinearBlending();

int main(){
    ROI_AddImage( );
    LinearBlending( );
    // ROI_LinearBlending( );
    cv::waitKey(0);
    return 0;
}

// 利用感兴趣区域ROI实现图像叠加
/*
 * 我们要在原始图像中贴上一张灰度图像
 * 所以先要指定贴在哪里,用cv::Rect指定
 * 然后用.copyTo实现贴图
 */
bool  ROI_AddImage(){
    cv::Mat srcImage1= cv::imread("../dota_pa.jpg");
    // 加载logo图像
    cv::Mat logoImage= cv::imread("../dota_logo.jpg");
    // 加载掩模必须是灰度图
    cv::Mat mask= cv::imread("../dota_logo.jpg",0);
    // Rect在原图设定ROI区域将掩膜拷贝到ROI区域
    logoImage.copyTo(srcImage1(cv::Rect(50,200, logoImage.cols, logoImage.rows)),mask);
    logoImage.copyTo(srcImage1(cv::Rect(300,200, logoImage.cols, logoImage.rows)));
    /**
    * copyTo()函数原型
    * copyTo(roi , mask)
    * A.copyTo(B);可以得到和A一毛一样的矩阵B。
    * 重构函数copyTo(roi,mask)的MASK可以实现蒙版功能
    * image.copyTo(imageROI),作用是把image的内容复制粘贴到imageROI上
    * image.copyTo(img2, mask);
    * １、提取感兴趣区,用预先制作的感兴趣区掩模与待处理图像相乘,得到感兴趣区图像,感兴趣区内图像值保持不变,而区外图像值都为0。
    * ２、屏蔽作用,用掩模对图像上某些区域作屏蔽,使其不参加处理或不参加处理参数的计算,或仅对屏蔽区作处理或统计。
    * ３、结构特征提取,用相似性变量或图像匹配方法检测和提取图像中与掩模相似的结构特征。
    * ４、特殊形状图像的制作。
    * 原图(image)与掩膜(mask)进行与运算后得到了结果图（img2）。比如1 & 1 = 1；1 & 0 = 0；　mask像素的值是非0的，我就拷贝它，否则不拷贝。
    * image.copyTo(imageROI,mask),作用是把mask和imageROI重叠以后把mask中像素值为0（黑色）的点变为透明这样就会只显示image中的其它点了
    * 不管是灰度图像还是彩色图像，只有掩膜中像素值为0黑色的点会对图像产生掩盖效果。
    */
    imshow("srcImage1",srcImage1);
    return true;
}


// 描述：利用cv::addWeighted（）函数实现图像线性混合
bool  LinearBlending(){
    cv::Mat srcImage2, srcImage3, dstImage;
    // 【1】读取图像 ( 两幅图片需为同样的类型和尺寸 )
    srcImage2 = cv::imread("../mogu.jpg");
    // imshow( "srcImage2", srcImage2 );
    srcImage3 = cv::imread("../rain.jpg");
    // imshow( "srcImage3", srcImage3 );

    // 【2】进行图像混合加权操作
    addWeighted( srcImage2, 0.33, srcImage3, 0.33, 0.0, dstImage);
    /*
     * void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dytpe=-1)
    第一个参数，InputArray类型的src1，表示需要加权的第一个数组，填一个Mat。
    第二个参数，double类型的alpha，表示第一个数组的权重。
    第三个参数，InputArray类型的src2，表示需要加权的第二个数组，需要和第一个数组拥有同样的尺寸和通道数。
    第四个参数，double类型的beta，表示第二个数组的权重。
    第五个参数，double类型的gamma，一个加到权重总和上的标量值
    第六个参数，OutputArray类型的dst，输出的数组，拥有同样的尺寸和通道数。
    第七个参数，int类型的dtype，输出阵列的可选深度，默认值-1。
     */

    // 【3】显示原图窗口
    imshow( "dstImage", dstImage );

    cv::Mat image_lidar, image_ndt, image_map, add_image;
    image_lidar = cv::imread("../lidar.png");
    cv::resize(image_lidar, image_lidar, cv::Size(1080,1080),0,0,CV_INTER_LINEAR);
    std::cout << "image_lidar " << image_lidar.size() << std::endl;
    image_ndt = cv::imread("../ndt.png");
    cv::resize(image_ndt, image_ndt, cv::Size(1080,1080),0,0,CV_INTER_LINEAR);

    std::cout << "image_ndt " << image_ndt.size() << std::endl;
    image_map = cv::imread("../map.png");
    cv::resize(image_map, image_map, cv::Size(1080,1080),0,0,CV_INTER_LINEAR);

    std::cout << "image_map " << image_map.size() << std::endl;
    addWeighted( image_lidar, 1, image_ndt, 1, 0.0, add_image);
    addWeighted( add_image, 1, image_map, 1, 0.0, add_image);
    imshow( "add_image", add_image);
    return true;

}

// 描述：线性混合实现函数,指定区域线性图像混合.利用cv::addWeighted（）函数结合定义
//			  感兴趣区域ROI，实现自定义区域的线性混合
bool  ROI_LinearBlending(){
    cv::Mat srcImage4= cv::imread("../dota_pa.jpg",1);
    imshow("dota_pa", srcImage4);
    cv::Mat logoImage= cv::imread("../dota_logo.jpg");
    imshow("dota_logo", logoImage);

    //【2】定义一个Mat类型并给其设定ROI区域
    cv::Mat imageROI;
    //方法一
    imageROI= srcImage4(cv::Rect(200,250,logoImage.cols,logoImage.rows));
    //方法二
    // imageROI= srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

    //【3】将logo加到原图上
    addWeighted(imageROI,0.5,logoImage,0.3,0.,imageROI);

    //【4】显示结果
    imshow("srcImage4",srcImage4);

    return true;
}


